package com.taotao.job.schedule.wheeltimer;

import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 时间轮实现
 *
 * @author fanbeibei
 * @version 1.0
 * @date 2020/12/15 17:16
 */
@Slf4j
public class WheelTimer {
    /**
     * 用于拉起调度的时间轮
     */
    private HashedWheelTimer schduleWheelTimer;

    private ExecutorService threadPool;

    /**
     * @param tickDuration  每个刻度时间间隔
     * @param unit
     * @param ticksPerWheel 时间轮一圈的长度
     * @param threadPool    线程池
     */
    public WheelTimer(long tickDuration, TimeUnit unit, int ticksPerWheel, ExecutorService threadPool) {
        schduleWheelTimer = new HashedWheelTimer(tickDuration, unit, ticksPerWheel);
        this.threadPool = threadPool;
    }

    /**
     * @param tickDuration  每个刻度时间间隔
     * @param unit
     * @param ticksPerWheel 时间轮一圈的长度
     */
    public WheelTimer(long tickDuration, TimeUnit unit, int ticksPerWheel) {
        schduleWheelTimer = new HashedWheelTimer(tickDuration, unit, ticksPerWheel);
        threadPool = new ThreadPoolExecutor(20, 40,
                5, TimeUnit.MINUTES, new ArrayBlockingQueue<>(5000), new ThreadFactory() {

            AtomicInteger num = new AtomicInteger(0);

            @Override
            public Thread newThread(Runnable r) {
                return new Thread("WheelTimer-thread_" + num.incrementAndGet());
            }
        });
    }

    /**
     * @param task      任务
     * @param timestamp 任务执行的时间戳
     */
    public void addTask(WheelTimerTriggerTask task, long timestamp) {
        long delay = timestamp - System.currentTimeMillis();
        //小于50毫秒就直接触发不再放到时间轮
        if (delay <= 50) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    runTriggerTask(task);
                }
            });
        } else {
            schduleWheelTimer.newTimeout(new TimerTask() {
                @Override
                public void run(Timeout timeout) throws Exception {
                    runTriggerTask(task);
                }
            }, delay, TimeUnit.MILLISECONDS);
        }
    }

    public void stop() {
        schduleWheelTimer.stop();
        threadPool.shutdown();
    }

    private void runTriggerTask(WheelTimerTriggerTask task) {
        try {
            task.run();
        } catch (Exception e) {
            log.error(task + ", ", e);
        }
    }

}
